package com.spring.nio.verify;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;

/**
 * @author: YSFY
 * @date: 2021-06-16 17:29
 * @pacage_name: com.spring.nio.verify
 * @class_name: ServerSoLingerThread
 * @description: 服务端soLinger线程
 */
public class ServerSoLingerThread implements Runnable {
    @Override
    public void run() {
        OutputStream outputStream = null;
        InputStream inputStream = null;
        Socket socket = null;
        try {
            // 1、构建服务端，监听端口
            ServerSocket serverSocket = new ServerSocket();
            InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getByName("192.168.14.93"), 10090);
            serverSocket.bind(socketAddress);

            // 2、阻塞等待客户端连接
            socket = serverSocket.accept();
            socket.setSoLinger(Boolean.TRUE, 10);

            // 3、测试点1: 接收客户端数据, 客户端关闭连接后，服务端循环发起read()操作，验证connect reset 异常
            //this.receiveFromClientThenClientCloseThenLoopRead(inputStream = socket.getInputStream());

            // 4、测试点2: 接收客户端数据, 并等待客户端关闭连接(socket.close())，服务端尝试向客户端发送数据，验证出broken pipe(write failed)异常
            //this.receiveFromClientWaitClientCloseThenWrite(socket.getInputStream(), socket.getOutputStream());

            // 5、测试点3: 接收客户端数据, 向客户端回传数据，之后关闭服务端通道，客户端以轮询方式不断发起read()操作，验证connect reset异常
            this.sendToClientThenServerClose(socket.getInputStream(), socket.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 测试点1: 接收客户端数据, 客户端关闭连接后，服务端循环发起read()操作，验证connect reset 异常
     * @param inputStream
     * */
    private void receiveFromClientThenClientCloseThenLoopRead(InputStream inputStream) throws IOException, InterruptedException {
        int readSize = 0;
        byte[] bytes = new byte[1024];
        while ((readSize = inputStream.read(bytes)) > 0) {
            System.out.println("来自客户端数据, data = " + new String(bytes, 0, bytes.length));
            // 模拟服务端处理数据
            TimeUnit.MILLISECONDS.sleep(1000);
        }
    }

    /**
     * 测试点2: 接收客户端数据, 并等待客户端关闭连接(socket.close())，服务端尝试向客户端发送数据，验证出broken pipe(write failed)异常
     * @param inputStream
     * @param outputStream
     * */
    private void receiveFromClientWaitClientCloseThenWrite(InputStream inputStream, OutputStream outputStream) throws IOException, InterruptedException {
        int readSize = 0;
        byte[] bytes = new byte[1024];
        while ((readSize = inputStream.read(bytes)) > 0) {
            System.out.println("来自客户端数据, data = " + new String(bytes, 0, bytes.length));
            // 模拟服务端处理数据
            TimeUnit.MILLISECONDS.sleep(1000);
            break;
        }

        // 睡眠五秒等待客户端关闭，然后服务端尝试写入数据
        TimeUnit.MILLISECONDS.sleep(5000);

        // 尝试发送数据
        while (true) {
            outputStream.write("接收客户端数据, 并等待客户端关闭连接(socket.close())，服务端尝试向客户端发送数据，验证出broken pipe(write failed)异常".getBytes(Charset.forName("utf-8")));
            outputStream.flush();
        }
    }

    /**
     * 测试点3:  接收客户端数据, 向客户端回传数据，之后关闭服务端通道，客户端以轮询方式不断发起read()操作，验证connect reset异常
     * @param inputStream
     * @param outputStream
     * */
    private void sendToClientThenServerClose(InputStream inputStream, OutputStream outputStream) throws InterruptedException, IOException {
        // 向客户端发送数据，发送完成后，服务端关闭socket，客户端接收到数据后继续尝试调用read()操作验证 connect reset异常
        outputStream.write("测试点3: 接收客户端数据, 向客户端回传数据，之后关闭服务端通道，客户端以轮询方式不断发起read()操作，验证connect reset异常".getBytes(Charset.forName("utf-8")));
        outputStream.flush();
    }
}
